package db

import (
	"context"
	"database/sql"
	"iris-wms/apps/admin/web/dto/dout"
	"iris-wms/common/model"
	"iris-wms/common/pkg/db/querier"
	"iris-wms/common/tools"
	"log"
	"os"
	"reflect"
	"time"

	"github.com/kataras/golog"
	"github.com/yitter/idgenerator-go/idgen"
	"gorm.io/driver/mysql"
	"gorm.io/gen"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
)

var GDB1 *gorm.DB
var GDB2 *gorm.DB

func InitGDb1() {
	selectList := tools.GConfig.App.Mysql.SelectList
	if len(selectList) == 0 {
		GDB1 = conn(tools.GConfig.App.Mysql.Db1)
	} else {
		for _, v := range selectList {
			// TODO 使用反射获取字段
			if v == "Db1" {
				GDB1 = conn(tools.GConfig.App.Mysql.Db1)
			}
			if v == "Db2" {
				GDB2 = conn(tools.GConfig.App.Mysql.Db2)
			}
		}
	}
	generate()
}

// // 创建数据库
// _, err = db.Exec("CREATE DATABASE IF NOT EXISTS test_db")
// if err != nil {
//     panic(err.Error())
// }
// fmt.Println("Successfully created database")

// // 删除数据库
// _, err = db.Exec("DROP DATABASE IF EXISTS test_db")
// if err != nil {
//     panic(err.Error())
// }
// fmt.Println("Successfully dropped database")

func conn(info tools.DBInfo) *gorm.DB {
	var (
		err   error
		db    *gorm.DB
		sqlDB *sql.DB
		// dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	)

	// gstps://gorm.io/zh_CN/docs/gorm_config.html Gorm2的连接配置
	if db, err = gorm.Open(mysql.Open(tools.GConfig.DBConnUrl(info)), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true, // 迁移数据库表时禁用外键约束
		Logger: logger.New(
			log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
			// tools.GetLogger(),
			logger.Config{
				SlowThreshold: time.Second, // Slow SQL threshold
				LogLevel: func() logger.LogLevel {
					if tools.GConfig.App.Gorm.Debug {
						return logger.Info
					} else {
						return logger.Error
					}
				}(),
				IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
				Colorful:                  true, // Disable color
			}),
		// 命名约定
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "",
			SingularTable: true, // 一个坑，不设置这个参数，gorm会把表名转义后加个s，导致找不到数据库的表
		},
	}); err != nil {
		goto ERR
	}
	if sqlDB, err = db.DB(); err != nil {
		goto ERR
	}
	sqlDB.SetMaxOpenConns(info.MaxOpenConns)
	sqlDB.SetConnMaxIdleTime(time.Duration(info.MaxIdleConns))
	// data, _ := json.Marshal(sqlDB.Stats())

	if err = db.Callback().Create().Before("gorm:create").Register("created_before", createBefore); err != nil {
		goto ERR
	}
	return db
ERR:
	golog.Fatalf("~~> MySQL start err:%s", err.Error())
	return nil
}

func createBefore(db *gorm.DB) {
	if db.Statement.Schema != nil {
		for _, field := range db.Statement.Schema.Fields {
			switch db.Statement.ReflectValue.Kind() {
			case reflect.Slice, reflect.Array:
				for i := 0; i < db.Statement.ReflectValue.Len(); i++ {
					if field.Name == "ID" {
						var value = idgen.NextId()
						id, _ := field.ValueOf(context.Background(), db.Statement.ReflectValue.Index(i))
						if id.(uint) > 0 {
							value = int64(id.(uint))
						}
						if err := field.Set(context.Background(),
							db.Statement.ReflectValue.Index(i), value); err != nil {
							golog.Errorf("[ID] Slice: grom global callback err:%v", err)
						}
					}
				}
			case reflect.Struct:
				if field.Name == "ID" {
					var value = idgen.NextId()
					id, _ := field.ValueOf(context.Background(), db.Statement.ReflectValue)
					if id.(uint) > 0 {
						value = int64(id.(uint))
					}
					if err := field.Set(context.Background(),
						db.Statement.ReflectValue, value); err != nil {
						golog.Errorf("[ID] Struct: grom global callback err:%v", err)
					}
				}
			}
		}
	}
}

func generate() {
	if tools.GConfig.App.Gorm.GenerateDao {
		generateDao()
	}
	if tools.GConfig.App.Gorm.AutoMigrate {
		generateAllTable()
	}
	if tools.GConfig.App.Gorm.InitData {
		initTableData()
	}
}

// 生成 dao
func generateDao() {
	g := gen.NewGenerator(gen.Config{
		OutPath: tools.GConfig.App.Gorm.OutPath,                                     // go.mod 为根目录
		Mode:    gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
	})

	// gormdb, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
	g.UseDB(GDB1) // reuse your gorm db

	// Generate basic type-safe DAO API for struct `model.User` following conventions
	g.ApplyBasic(
		// 用数据库生成struct
		// g.GenerateAllTable()...

		// 使用stuct生成table
		dout.AdminUserOut{},
		dout.AdminRuleOut{}, dout.AdminPolicyOut{},
		dout.AdminMenuOut{}, dout.AdminButtonOut{},
		dout.AdminRoleOut{},
		dout.AdminLogOut{},
		dout.AdminDictOut{},
		// wms
		dout.WmsGoodsCateOut{}, dout.WmsGoodsOut{},
		dout.WmsWarehouseOut{},
		dout.WmsCustomerOut{}, dout.WmsSupplierOut{}, dout.WmsCarrierOut{},
		dout.WmsBillOut{}, dout.WmsBillGoodsOut{}, dout.WmsBillLogisticsOut{}, dout.WmsInventoryOut{},
		// report
		dout.WmsReportOfWarehouseOut{}, dout.WmsReportOfGoodsOut{},
	)
	g.ApplyInterface(func(querier.InventoryDimGoods) {}, dout.WmsReportOfGoodsOut{})
	g.ApplyInterface(func(querier.InventoryDimWarehouse) {}, dout.WmsReportOfWarehouseOut{})

	// Generate the code
	g.Execute()
}

func generateAllTable() {
	GDB1.AutoMigrate(
		model.AdminUser{},
		model.AdminRole{}, model.AdminRoleMenu{},
		model.AdminMenu{}, model.AdminMenuMeta{}, model.AdminMenuButton{},
		model.AdminRule{}, model.AdminPolicy{}, model.AdminRolePolicy{},
		model.AdminLog{}, model.AdminButton{},
		model.AdminDict{},
		// wms
		model.WmsGoodsCate{}, model.WmsGoods{}, model.WmsGoodsImage{},
		model.WmsWarehouse{},
		model.WmsCustomer{}, model.WmsSupplier{}, model.WmsCarrier{},
		model.WmsBill{}, model.WmsBillGoods{}, model.WmsBillLogistics{}, model.WmsInventory{},
	)
}

func initTableData() {
	// user
	var userList = [...]model.AdminUser{
		{Model: gorm.Model{ID: 1}, Username: tools.GConfig.App.RootUsername, Password: tools.GConfig.App.RootPassword, Name: "超级用户", Enable: 1, Age: 19, Gender: model.Gender_Female},
	}
	// buttons
	var buttonList = [...]model.AdminButton{
		{Model: gorm.Model{ID: 1}, Uid: 1, Name: "刷新表", Place: model.Top_Right},
		{Model: gorm.Model{ID: 2}, Uid: 1, Name: "导入", Place: model.Top_Right},
		{Model: gorm.Model{ID: 3}, Uid: 1, Name: "导出", Place: model.Top_Right},
		{Model: gorm.Model{ID: 4}, Uid: 1, Name: "打印", Place: model.Top_Right},
		{Model: gorm.Model{ID: 5}, Uid: 1, Name: "最大化", Place: model.Top_Right},
		{Model: gorm.Model{ID: 6}, Uid: 1, Name: "自定义列", Place: model.Top_Right},
		//
		{Model: gorm.Model{ID: 7}, Uid: 1, Name: "创建", Place: model.Top_Left, Icon: "vxe-icon-add", Func: "create"},
		{Model: gorm.Model{ID: 8}, Uid: 1, Name: "编辑", Place: model.Table_Inner, Func: "edit"},
		{Model: gorm.Model{ID: 9}, Uid: 1, Name: "删除", Place: model.Table_Inner, Func: "deletes", Tip: 1, TipContent: "警告：数据删除后不可恢复！确定删除吗？"},
		{Model: gorm.Model{ID: 10}, Uid: 1, Name: "复制行", Place: model.Table_Inner, Func: "copy"},
		{Model: gorm.Model{ID: 11}, Uid: 1, Name: "授权", Place: model.Table_Inner, Func: "grant"},
		{Model: gorm.Model{ID: 12}, Uid: 1, Name: "重置密码", Place: model.Table_Inner, Func: "resetPassword"},
		// wms bill
		{Model: gorm.Model{ID: 13}, Uid: 1, Name: "创建入库单", Place: model.Top_Left, Icon: "vxe-icon-add", Func: "create"},
		{Model: gorm.Model{ID: 14}, Uid: 1, Name: "创建出库单", Place: model.Top_Left, Icon: "vxe-icon-add", Func: "create"},
		{Model: gorm.Model{ID: 15}, Uid: 1, Name: "创建移库单", Place: model.Top_Left, Icon: "vxe-icon-add", Func: "create"},
		{Model: gorm.Model{ID: 16}, Uid: 1, Name: "创建盘点单", Place: model.Top_Left, Icon: "vxe-icon-add", Func: "create"},
		{Model: gorm.Model{ID: 17}, Uid: 1, Name: "打印", Place: model.Table_Inner, Func: "print", Memo: "单据打印"},
		{Model: gorm.Model{ID: 18}, Uid: 1, Name: "详情", Place: model.Table_Inner, Func: "detail"},
		// wms warehouse
		{Model: gorm.Model{ID: 1000}, Uid: 1, Name: "创建仓库", Place: model.Other},
		{Model: gorm.Model{ID: 1001}, Uid: 1, Name: "创建库区", Place: model.Other},
		{Model: gorm.Model{ID: 1002}, Uid: 1, Name: "修改仓库", Place: model.Other, Memo: "仓库按钮"},
		{Model: gorm.Model{ID: 1003}, Uid: 1, Name: "删除仓库", Place: model.Other, Memo: "仓库按钮"},
	}

	// bill
	var inStatus = "bill-inbound-status"
	var inType = "bill-inbound-type"
	var outStatus = "bill-outbound-status"
	var outType = "bill-outbound-type"
	var dictList = [...]model.AdminDict{
		// inbound
		{Model: gorm.Model{ID: 1}, Uid: 1, Mark: inStatus, Code: "wrk", Label: "未入库", Memo: ""},
		{Model: gorm.Model{ID: 2}, Uid: 1, Mark: inStatus, Code: "zt", Label: "在途(已发未入库)", Memo: ""},
		{Model: gorm.Model{ID: 3}, Uid: 1, Mark: inStatus, Code: "bfrk", Label: "部分入库", Memo: ""},
		{Model: gorm.Model{ID: 4}, Uid: 1, Mark: inStatus, Code: "rkwc", Label: "入库完成", Memo: ""},
		//
		{Model: gorm.Model{ID: 5}, Uid: 1, Mark: inType, Code: "cgrk", Label: "采购入库", Memo: ""},
		{Model: gorm.Model{ID: 6}, Uid: 1, Mark: inType, Code: "wxrk", Label: "外协入库", Memo: ""},
		{Model: gorm.Model{ID: 7}, Uid: 1, Mark: inType, Code: "thrk", Label: "退货入库", Memo: ""},
		{Model: gorm.Model{ID: 8}, Uid: 1, Mark: inType, Code: "qtrk", Label: "其他入库", Memo: ""},
		{Model: gorm.Model{ID: 9}, Uid: 1, Mark: inType, Code: "yhrk", Label: "移货入库", Tag: 1, Memo: ""},
		{Model: gorm.Model{ID: 10}, Uid: 1, Mark: inType, Code: "pyrk", Label: "盘盈入库", Tag: 1, Memo: ""},
		// outbound
		{Model: gorm.Model{ID: 11}, Uid: 1, Mark: outStatus, Code: "wfh", Label: "未发库", Memo: ""},
		{Model: gorm.Model{ID: 12}, Uid: 1, Mark: outStatus, Code: "bffh", Label: "部分发货", Memo: ""},
		{Model: gorm.Model{ID: 13}, Uid: 1, Mark: outStatus, Code: "fhwc", Label: "发货完成", Memo: ""},
		//
		{Model: gorm.Model{ID: 14}, Uid: 1, Mark: outType, Code: "xsck", Label: "销售出库", Memo: ""},
		{Model: gorm.Model{ID: 15}, Uid: 1, Mark: outType, Code: "wxck", Label: "外协出库", Memo: ""},
		{Model: gorm.Model{ID: 16}, Uid: 1, Mark: outType, Code: "qtck", Label: "其他出库", Memo: ""},
		{Model: gorm.Model{ID: 17}, Uid: 1, Mark: outType, Code: "yhck", Label: "移货出库", Tag: 1, Memo: ""},
		{Model: gorm.Model{ID: 18}, Uid: 1, Mark: outType, Code: "pkck", Label: "盘亏出库", Tag: 1, Memo: ""},
	}
	GDB1.Transaction(func(tx *gorm.DB) error {
		for _, v := range userList {
			pwd, err := tools.GeneratePassword(v.Password)
			if err != nil {
				return err
			}
			v.Password = pwd
			err = tx.Where(model.AdminUser{Model: gorm.Model{ID: v.ID}}).FirstOrCreate(&v).Error
			if err != nil {
				return err
			}
		}
		// button
		for _, v := range buttonList {
			err := tx.Where(model.AdminButton{Model: gorm.Model{ID: v.ID}}).FirstOrCreate(&v).Error
			if err != nil {
				return err
			}
		}
		// bill
		for _, v := range dictList {
			err := tx.Where(model.AdminDict{Model: gorm.Model{ID: v.ID}}).FirstOrCreate(&v).Error
			if err != nil {
				return err
			}
		}
		return nil
	})
}
